package cn.hg.solon.youcan.framework.web.admin.filter;

import java.util.HashMap;
import java.util.Map;

import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.exception.ValidateException;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.text.CharSequenceUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.http.meta.HttpStatus;
import org.noear.solon.core.NvMap;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.ModelAndView;
import org.noear.solon.core.handle.Result;
import org.noear.solon.validation.ValidatorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.dev33.satoken.exception.ApiDisabledException;
import cn.dev33.satoken.exception.DisableServiceException;
import cn.dev33.satoken.exception.NotBasicAuthException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.exception.NotSafeException;
import cn.dev33.satoken.exception.SaJsonConvertException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.exception.StopMatchException;
import cn.hg.solon.youcan.common.constant.AppConstants;
import cn.hg.solon.youcan.common.exception.ServiceException;
import cn.hg.solon.youcan.common.util.ContextPathUtil;
import cn.hg.solon.youcan.common.util.RequestUtil;
import cn.hg.solon.youcan.framework.web.pear.LayuiDtree;
import cn.hg.solon.youcan.framework.web.pear.LayuiPage;
import cn.hg.solon.youcan.framework.web.pear.LayuiTransferData;
import cn.hg.solon.youcan.framework.web.pear.LayuiTree;
import cn.hg.solon.youcan.framework.web.pear.LayuiTreeSelect;
import cn.hg.solon.youcan.framework.web.pear.LayuiTreeTable;

/**
 * @author 胡高
 */
public class ActionResultHandler {
    /*
     * 错误页面Mapping
     */
    static Map<Integer, String> ERROR_VIEWS = MapUtil.createMap(HashMap.class);

    /*
     * 错误信息Mapping
     */
    private static Map<Integer, String> ERROR_MESSAGE = MapUtil.createMap(HashMap.class);

    static {
        ERROR_VIEWS.put(HttpStatus.HTTP_UNAUTHORIZED, "/admin/_error/401.html");
        ERROR_VIEWS.put(HttpStatus.HTTP_FORBIDDEN, "/admin/_error/403.html");
        ERROR_VIEWS.put(HttpStatus.HTTP_NOT_FOUND, "/admin/_error/404.html");
        ERROR_VIEWS.put(HttpStatus.HTTP_INTERNAL_ERROR, "/admin/_error/500.html");

        ERROR_MESSAGE.put(HttpStatus.HTTP_BAD_REQUEST, "请求异常！");
        ERROR_MESSAGE.put(HttpStatus.HTTP_UNAUTHORIZED, "登录超时，请重新登录！");
        ERROR_MESSAGE.put(HttpStatus.HTTP_FORBIDDEN, "您没有此操作的权限！");
        ERROR_MESSAGE.put(HttpStatus.HTTP_NOT_FOUND, "请求路径异常！");
        ERROR_MESSAGE.put(HttpStatus.HTTP_BAD_METHOD, "请求方法异常！");
        ERROR_MESSAGE.put(HttpStatus.HTTP_NOT_ACCEPTABLE, "返回结果异常！");
        ERROR_MESSAGE.put(HttpStatus.HTTP_GONE, "请求错误");
        ERROR_MESSAGE.put(HttpStatus.HTTP_INTERNAL_ERROR, "系统异常！");
    }

    /*
     * 登录地址
     */
    private static String loginUrl = "/admin/login";
    private static String loginUrlCtx = ContextPathUtil.getContextPath() + loginUrl;

    /*
     * 首页地址
     */
    private static String indexUrl = "/admin";
    private static String indexUrlCtx = ContextPathUtil.getContextPath() + indexUrl;
    private static String indexUrl2 = "/admin/";
    private static String indexUrlCtx2 = ContextPathUtil.getContextPath() + indexUrl2;

    /*
     * 日志
     */
    static Logger log = LoggerFactory.getLogger(ActionResultHandler.class);

    public static void handle(Context ctx, Throwable e) throws Throwable {
        // 可能上级链已完成处理
        if (ctx.getHandled()) {
            return;
        }

        Integer code = ctx.status();
        String message = null;

        if (ObjUtil.isNotNull(e)) {
            if (e instanceof SaTokenException) {
                /*
                 * 针对SaTokenException特别处理
                 */
                message = e.getMessage();
                if (e instanceof ApiDisabledException || e instanceof DisableServiceException) {
                    code = HttpStatus.HTTP_GONE;
                } else if (e instanceof NotBasicAuthException || e instanceof NotLoginException) {
                    code = HttpStatus.HTTP_UNAUTHORIZED;
                } else if (e instanceof NotPermissionException || e instanceof NotRoleException
                    || e instanceof NotSafeException) {
                    code = HttpStatus.HTTP_FORBIDDEN;
                } else if (e instanceof SaJsonConvertException || e instanceof StopMatchException) {
                    code = HttpStatus.HTTP_BAD_REQUEST;
                }
                if (ERROR_MESSAGE.containsKey(code)) {
                    message = ERROR_MESSAGE.get(code);
                }
            } else if (ctx.result instanceof ValidatorException) {
                /*
                 * 校验异常处理
                 */
                message = e.getMessage();
                code = HttpStatus.HTTP_BAD_REQUEST;
            } else if (ctx.result instanceof ValidateException) {
                /*
                 * 校验异常处理
                 */
                message = e.getMessage();
                code = HttpStatus.HTTP_BAD_REQUEST;
            } else if (ctx.result instanceof ServiceException) {
                /*
                 * 业务异常处理
                 */
                log.error(e.getMessage(), e);

                message = e.getMessage();
                code = ((ServiceException)ctx.result).getCode();
            } else {
                // 打印异常日志
                log.error(e.getMessage(), e);

                code = AppConstants.RETURN_CODE_VALUE_FAIL;
                message = e.getMessage();
            }
        }

        try {
            /*
             * 结果处理
             */
            if (RequestUtil.isAjaxRequest(ctx)) {
                handleAjaxResult(ctx, code, message);
            } else {
                handleActionResult(ctx, code, message);
            }
        } catch (Throwable ex) {
            throw ex;
        }
    }

    private static void handleActionResult(Context ctx, int code, String message) throws Throwable {
        if (code == HttpStatus.HTTP_UNAUTHORIZED
            && (CharSequenceUtil.equals(indexUrl, ctx.path()) || CharSequenceUtil.equals(indexUrlCtx, ctx.path())
                || CharSequenceUtil.equals(indexUrl2, ctx.path()) || CharSequenceUtil.equals(indexUrlCtx2, ctx.path())
                )) {
            /*
             * 未登录的情况下访问admin首页，直接跳转到login页面；
             */
            ctx.redirect(loginUrlCtx);
        } else if (code == HttpStatus.HTTP_UNAUTHORIZED
            && (CharSequenceUtil.equals(loginUrl, ctx.path()) || CharSequenceUtil.equals(loginUrlCtx, ctx.path()))) {

            String view = ERROR_VIEWS.get(HttpStatus.HTTP_UNAUTHORIZED);
            if (ERROR_VIEWS.containsKey(code)) {
                view = ERROR_VIEWS.get(code);
            }

            /*
             * 未登录的情况下访问admin其它页面，则显示未登录或登录超时提示页面
             */
            try {
                ctx.result = new ModelAndView(view);
                ctx.render(view, null);
            } catch (Throwable e) {
                log.error("Render view error: view={}, code={}, message={}", view, code, message);
            }
        } else {
            String view = ERROR_VIEWS.get(HttpStatus.HTTP_INTERNAL_ERROR);
            if (ERROR_VIEWS.containsKey(code)) {
                view = ERROR_VIEWS.get(code);
            }

            try {
                ctx.result = new ModelAndView(view);
                ctx.render(view, new NvMap().set("code", StrUtil.toString(code)).set("description", message));
            } catch (Throwable e) {
                throw e;
            }
        }
    }

    private static void handleAjaxResult(Context ctx, int code, String message) throws Throwable {
        Class<?> retrunClz = ctx.action().method().getReturnType();
        if (ObjUtil.isNull(retrunClz)) {
            // 如果返回类型为Null
            ctx.render(Result.failure(code, message));
        }

        Object result = null;
        /*
         * 不同的方法返回不同的包装类
         */
        if (retrunClz.isAssignableFrom(LayuiDtree.class)) {
            result = new LayuiDtree<>(code, message);
        } else if (retrunClz.isAssignableFrom(LayuiPage.class)) {
            result = new LayuiPage<>(code, message);
        } else if (retrunClz.isAssignableFrom(LayuiTransferData.class)) {
            result = new LayuiTransferData<>(ListUtil.empty(), "", "");
        } else if (retrunClz.isAssignableFrom(LayuiTree.class)) {
            result = new LayuiTree<>(ListUtil.empty(), "", "", "", "", "");
        } else if (retrunClz.isAssignableFrom(LayuiTreeSelect.class)) {
            result = new LayuiTreeSelect<>(ListUtil.empty(), "", "", "", "");
        } else if (retrunClz.isAssignableFrom(LayuiTreeTable.class)) {
            result = new LayuiTreeTable<>(code, message);
        } else if (retrunClz.isAssignableFrom(Result.class)) {
            result = Result.failure(code, message);
        } else {
            result = Result.failure(code, message);
        }

        ctx.result = result;
        ctx.render(result);
    }
}
